/*
 * Copyright (c) 2008 Eric Berry <elberry@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package pulpcore.templates;

import java.awt.Component;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.swing.JComponent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.spi.project.ui.support.ProjectChooser;
import org.netbeans.spi.project.ui.templates.support.Templates;
import org.openide.WizardDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import pulpcore.utils.ProjectUtils;

public class QuickWizardIterator implements WizardDescriptor./*Progress*/InstantiatingIterator {

	  private int index;
	  private WizardDescriptor.Panel[] panels;
	  private WizardDescriptor wiz;

	  public QuickWizardIterator() {
	  }

	  public static QuickWizardIterator createIterator() {
			 return new QuickWizardIterator();
	  }

	  private WizardDescriptor.Panel[] createPanels() {
			 return new WizardDescriptor.Panel[]{
					new QuickWizardPanel(),
			 };
	  }

	  private String[] createSteps() {
			 return new String[]{
					NbBundle.getMessage(QuickWizardIterator.class, "LBL_CreateProjectStep")
			 };
	  }

	  public Set/*<FileObject>*/ instantiate(/*ProgressHandle handle*/) throws IOException {
			 Set<FileObject> resultSet = new LinkedHashSet<FileObject>();
			 File dirF = FileUtil.normalizeFile((File) wiz.getProperty("projdir"));
			 dirF.mkdirs();

			 FileObject template = Templates.getTemplate(wiz);
			 FileObject dir = FileUtil.toFileObject(dirF);
			 unZipFile(template.getInputStream(), dir);

			 // Always open top dir as a project:
			 resultSet.add(dir);
			 // Look for nested projects to open as well:
			 Enumeration<? extends FileObject> e = dir.getFolders(true);
			 while (e.hasMoreElements()) {
					FileObject subfolder = e.nextElement();
					if (ProjectManager.getDefault().isProject(subfolder)) {
						resultSet.add(subfolder);
					}
			 }

			 File parent = dirF.getParentFile();
			 if (parent != null && parent.exists()) {
					ProjectChooser.setProjectsFolder(parent);
			 }
			 String projectName = (String) wiz.getProperty("name");
			 try {
					ProjectUtils.fixProjectFiles(dirF, projectName, ProjectUtils.Type.Quick);
			 } catch (Exception ex) {
					ex.printStackTrace();
			 }
			 return resultSet;
	  }

	  public void initialize(WizardDescriptor wiz) {
			 this.wiz = wiz;
			 index = 0;
			 panels = createPanels();
			 // Make sure list of steps is accurate.
			 String[] steps = createSteps();
			 for (int i = 0; i < panels.length; i++) {
					Component c = panels[i].getComponent();
					if (steps[i] == null) {
						// Default step name to component name of panel.
						// Mainly useful for getting the name of the target
						// chooser to appear in the list of steps.
						steps[i] = c.getName();
					}
					if (c instanceof JComponent) { // assume Swing components
						JComponent jc = (JComponent) c;
						// Step #.
						jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
						// Step name (actually the whole list for reference).
						jc.putClientProperty("WizardPanel_contentData", steps);
					}
			 }
	  }

	  public void uninitialize(WizardDescriptor wiz) {
			 this.wiz.putProperty("projdir", null);
			 this.wiz.putProperty("name", null);
			 this.wiz = null;
			 panels = null;
	  }

	  public String name() {
			 return MessageFormat.format("{0} of {1}",
							 new Object[]{new Integer(index + 1), new Integer(panels.length)});
	  }

	  public boolean hasNext() {
			 return index < panels.length - 1;
	  }

	  public boolean hasPrevious() {
			 return index > 0;
	  }

	  public void nextPanel() {
			 if (!hasNext()) {
					throw new NoSuchElementException();
			 }
			 index++;
	  }

	  public void previousPanel() {
			 if (!hasPrevious()) {
					throw new NoSuchElementException();
			 }
			 index--;
	  }

	  public WizardDescriptor.Panel current() {
			 return panels[index];
	  }

	  // If nothing unusual changes in the middle of the wizard, simply:
	  public final void addChangeListener(ChangeListener l) {
	  }

	  public final void removeChangeListener(ChangeListener l) {
	  }

	  private static void unZipFile(InputStream source, FileObject projectRoot) throws IOException {
			 try {
					ZipInputStream str = new ZipInputStream(source);
					ZipEntry entry;
					while ((entry = str.getNextEntry()) != null) {
						if (entry.isDirectory()) {
							  FileUtil.createFolder(projectRoot, entry.getName());
						} else {
							  FileObject fo = FileUtil.createData(projectRoot, entry.getName());
							  if ("nbproject/project.xml".equals(entry.getName())) {
									 // Special handling for setting name of Ant-based projects; customize as needed:
									 filterProjectXML(fo, str, projectRoot.getName());
							  } else {
									 writeFile(str, fo);
							  }
						}
					}
			 } finally {
					source.close();
			 }
	  }

	  private static void writeFile(ZipInputStream str, FileObject fo) throws IOException {
			 OutputStream out = fo.getOutputStream();
			 try {
					FileUtil.copy(str, out);
			 } finally {
					out.close();
			 }
	  }

	  private static void filterProjectXML(FileObject fo, ZipInputStream str, String name) throws IOException {
			 try {
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					FileUtil.copy(str, baos);
					Document doc = XMLUtil.parse(new InputSource(new ByteArrayInputStream(baos.toByteArray())), false, false, null, null);
					NodeList nl = doc.getDocumentElement().getElementsByTagName("name");
					if (nl != null) {
						for (int i = 0; i < nl.getLength(); i++) {
							  Element el = (Element) nl.item(i);
							  if (el.getParentNode() != null && "data".equals(el.getParentNode().getNodeName())) {
									 NodeList nl2 = el.getChildNodes();
									 if (nl2.getLength() > 0) {
											nl2.item(0).setNodeValue(name);
									 }
									 break;
							  }
						}
					}
					OutputStream out = fo.getOutputStream();
					try {
						XMLUtil.write(doc, out, "UTF-8");
					} finally {
						out.close();
					}
			 } catch (Exception ex) {
					Exceptions.printStackTrace(ex);
					writeFile(str, fo);
			 }

	  }
}
